/*
* Poppy Game Engine - https://pgetemplate.googlecode.com/svn/
* -----------------------------------------------------------------------
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
*
* Copyright (c) 2008 Poppy
*/

#include "stdafx.h"
#include "CPSPDriver.h"

#ifdef PSP

namespace PGERenderer
{

	void CPSPDriver::CopyImage(const COPYDATA* data)
	{
		SendCommandToDatai(m_pData, TRXSBP, ((unsigned int)data->src) & 0xffffff);
		SendCommandToDatai(m_pData, TRXSBW, ((((unsigned int)data->src) & 0xff000000) >> 8) | data->srcw);
		SendCommandToDatai(m_pData, TRXSPOS, (data->sy << 10) | data->sx);
		SendCommandToDatai(m_pData, TRXDBP, ((unsigned int)data->dest) & 0xffffff);
		SendCommandToDatai(m_pData, TRXDBW, ((((unsigned int)data->dest) & 0xff000000) >> 8) | data->destw);
		SendCommandToDatai(m_pData, TRXDPOS, (data->dy << 10) | data->dx);
		SendCommandToDatai(m_pData, TRXSIZE, ((data->height-1) << 10) | (data->width-1));
		SendCommandToDatai(m_pData, TRXKICK, (data->psm ^ 0x03) ? 0 : 1);
	}
	
	void CPSPDriver::CopyImageToBuffer(COPYDATA* data)
	{
		data->dest = GetBuffer();
		CopyImage(data);
	}
	
	void* CPSPDriver::GetBuffer()
	{
		return (void*)(0x04000000 | (unsigned int)(m_pData->m_sDrawBuffer.m_pFrameBuffer));
	}

	void CPSPDriver::SetVertexType(unsigned int vt, unsigned int vtsize)
	{
		SendCommandToDatai(m_pData, VTYPE, vt);
	}

	void CPSPDriver::DrawVertex(PGEPRIMITIVE_TYPE prim, unsigned int count, LPVERTEX_BUFFER vertices)
	{
		SendCommandToDatai(m_pData, BASE, (((unsigned int)vertices) >> 8) & 0xf0000);
		SendCommandToDatai(m_pData, VADDR, ((unsigned int)vertices) & 0xffffff);
		SendCommandToDatai(m_pData, PRIM, (prim << 16)| count);
	}

	void CPSPDriver::DrawIndex(PGEPRIMITIVE_TYPE prim, unsigned int count, unsigned int vertexs, LPVERTEX_BUFFER vertices, LPINDEX_BUFFER indices)
	{
		SendCommandToDatai(m_pData, BASE, (((unsigned int)indices) >> 8) & 0xf0000);
		SendCommandToDatai(m_pData, IADDR, ((unsigned int)indices) & 0xffffff);
		SendCommandToDatai(m_pData, BASE, (((unsigned int)vertices) >> 8) & 0xf0000);
		SendCommandToDatai(m_pData, VADDR, ((unsigned int)vertices) & 0xffffff);
		SendCommandToDatai(m_pData, PRIM, (prim << 16)| count);
	}

	void CPSPDriver::DepthRange(int near, int far)
	{
		unsigned int max = (unsigned int)near + (unsigned int)far;
		int val = (int)((max >> 31) + max);
		float z = (float)(val >> 1);
		
		m_pData->m_sContexts.m_iNearPlane = near;
		m_pData->m_sContexts.m_iFarPlane = far;

		SendCommandToDataf(m_pData, ZSCALE, z - ((float)near));
		SendCommandToDataf(m_pData, ZPOS, z + ((float)m_pData->m_sContexts.m_iDepthOffset));

		if (near > far)
		{
			int temp = near;
			near = far;
			far = temp;
		}

		SendCommandToDataf(m_pData, NEARZ, near);
		SendCommandToDataf(m_pData, FARZ, far);
	}
				
	void CPSPDriver::FrontFace(int order)
	{
		if (order)
			SendCommandToDataf(m_pData, FFACE, 0);
		else
			SendCommandToDataf(m_pData, FFACE, 1);
	}
				
	void CPSPDriver::ShadeModel(int mode)
	{
		switch (mode)
		{
			case 1: SendCommandToDataf(m_pData, SHADE,1); break;
			default: SendCommandToDataf(m_pData, SHADE,0); break;
		}
	}
				
	void CPSPDriver::ClearState(EClearFlag flag, unsigned int color)
	{
		switch(flag)
		{
		case CLEAR_COLOR:
			m_pData->m_sContexts.m_uClearColor = (m_pData->m_sContexts.m_uClearColor & 0xff000000) | (color & 0xffffff);
			break;
		case CLEAR_STENCIL:
			m_pData->m_sContexts.m_uClearColor = (m_pData->m_sContexts.m_uClearColor & 0xffffff) | (color << 24);
			break;
		case CLEAR_COLOR_STENCIL:
			m_pData->m_sContexts.m_uClearColor = color;
			break;
		case CLEAR_DEPTH:
			m_pData->m_sContexts.m_uClearDepth = color & 0xffff;
			break;
		case CLEAR_AREA:
			m_pData->m_iClearArea = color;
			break; 
		case CLEAR_ENABLE:
			if(color)
				m_pData->m_bIsClear = true;
			else
				m_pData->m_bIsClear = false;
			break;
		}
	}

	void CPSPDriver::DrawRegion(int x, int y, int width, int height)
	{
		SendCommandToDatai(m_pData, REGION1, (y << 10) | x);
		SendCommandToDatai(m_pData, REGION2, (((y + height)-1) << 10) | ((x + width)-1));
	}

	void CPSPDriver::SetDither(const int* matrix)
	{
		SendCommandToDatai(m_pData, DTH0, (matrix[0] & 0x0f)|((matrix[1] & 0x0f) << 4)|((matrix[2] & 0x0f) << 8)|((matrix[3] & 0x0f) << 12));
		SendCommandToDatai(m_pData, DTH1, (matrix[4] & 0x0f)|((matrix[5] & 0x0f) << 4)|((matrix[6] & 0x0f) << 8)|((matrix[7] & 0x0f) << 12));
		SendCommandToDatai(m_pData, DTH2, (matrix[8] & 0x0f)|((matrix[9] & 0x0f) << 4)|((matrix[10] & 0x0f) << 8)|((matrix[11] & 0x0f) << 12));
		SendCommandToDatai(m_pData, DTH3, (matrix[12] & 0x0f)|((matrix[13] & 0x0f) << 4)|((matrix[14] & 0x0f) << 8)|((matrix[15] & 0x0f) << 12));
	}
	
	void* CPSPDriver::GetMemory(int size)
	{
		size = (size + 3) & 0xFFFFFFFC;
		unsigned int* orig_ptr = m_pData->m_sList.m_pCurrent;
		unsigned int* new_ptr = (unsigned int*)(((unsigned int)orig_ptr) + size + 8);

		orig_ptr[0] = (BASE << 24) | ((((unsigned int)new_ptr) >> 8) & 0xf0000);
		orig_ptr[1] = (JUMP << 24) | (((unsigned int)new_ptr) & 0xffffff);
		
		m_pData->m_sList.m_pCurrent = new_ptr;
		
		sceGeListUpdateStallAddr(m_pData->m_iGeListExecuted, new_ptr);

		return orig_ptr + 2;
	}

	void CPSPDriver::PatchDivide(unsigned int a0, unsigned int a1)
	{
		SendCommandToDatai(m_pData, PSUB, (a1 << 8)|a0);
	}
	
	void CPSPDriver::ColorMaterial(int a0)
	{
		SendCommandToDatai(m_pData, CMAT, a0);
	}
	
	void CPSPDriver::Specular(float power)
	{
		SendCommandToDataf(m_pData, SPOW, power);
	}
	
	void CPSPDriver::TexScale(float u, float v)
	{
		SendCommandToDataf(m_pData, USCALE, u);
		SendCommandToDataf(m_pData, VSCALE, v);
	}
	
	void CPSPDriver::Offset(unsigned int x, unsigned int y)
	{
		SendCommandToDatai(m_pData, OFFSETX, x << 4);
		SendCommandToDatai(m_pData, OFFSETY, y << 4);
	}
	
	void CPSPDriver::Viewport(int cx, int cy, int width, int height)
	{
		SendCommandToDataf(m_pData, XSCALE,(float)(width>>1));
		SendCommandToDataf(m_pData, YSCALE,(float)((-height)>>1));
		SendCommandToDataf(m_pData, XPOS,(float)cx);
		SendCommandToDataf(m_pData, YPOS,(float)cy);
	}
	
	void CPSPDriver::Scissor(int x, int y, int w, int h)
	{
		SendCommandToDataf(m_pData, SCISOR1, (y << 10) | x);
		SendCommandToDataf(m_pData, SCISOR2, ((h-1) << 10) | (w-1));
	}
	
}

#endif
